[contents] [prev] [next] [top] [bottom] (3 out of 6)

Module Concepts

Modules provide a mechanism for creating and managing multiple namespaces, similar to the packaging system in the Lisp language and the proposed "namespace" mechanism in C++. Each module has its own global scope, and variables that are declared global are only global within the boundaries of that module. The only real global names are the names of modules themselves.

This section describes the terms and concepts that underlie the use of modules in ScriptX:

  1. To export a name is to make a name that is defined in a given module available for import into other modules. Only names that are exported are public. Each name that is to be public must be explicitly exported.
  2. To import a name is to make a name that has been exported by another module available for use within a given module. Therefore, to access a name in another module requires two steps: that it be exported from that module and imported into this module. A module can import any name that is public.
  3. A name binding is an association between a name and a value cell in memory. A ScriptX module can be thought of as a collection of name bindings for objects in a program.
  4. A variable could be described as the set of name bindings (across modules) for a given value cell in memory. A variable is defined in only one module, the module in which it is initially assigned a value. Although a variable is visible and can be changed in all modules in which it has a name binding, it can be saved in a storage container only in the module which defines it.
  5. A module owns a variable if it defines it by assigning a value to the name that is associated with the variable. A definition is a statement of ownership which combines the declaration of a name and the assignment of a value to that name.
  6. Modules are not a security feature. Modules do not control access to objects in any way. The fact that a module owns a variable does not prevent a program passing a reference to the object that variable refers to on to a program that is compiling in another module. Modules only control access to objects by name.
As background reading for this section, you might want to review the ScriptX treatment of scope, lexical names, variables, and assignment from earlier sections of this volume.

Exporting Names

Every global name within a module is either exported (public) or unexported (private). By default, names are not exported. When you define a module, you specify which names are exported-that is, which names are available outside the module for import into other modules. Only names that are exported by some module can be imported by other modules.

Because a ScriptX variable can store a reference to any object, including classes and functions, exporting the name of a class or function makes those objects public, ready for import into other modules.

Figure 9-1: Exporting names from modules

A ScriptX module can export a name, even though it does not define the variable that is associated with that name. If a module exports names that are declared and defined by one of its client modules, it acts as an interface for that module. The section "Organizing Modules" on page 214 shows how this feature can be used to create a network of interface and implementation modules.

Importing Names

To access names in another module, you must export those names from that module and then import them into this module. Imported names appear in the new module just as if they had been declared there, and, if they have been defined as variables, their definitions come along as well. The importing module is considered to be using the first module, and the relationship between two modules is called a use relationship. Modules can use and be used by any number of other modules, as long as no circular use relationships are created.

In Figure 9-2, MyOtherModule uses MyModule. The names x, y, and z that are exported by MyModule are imported by MyOtherModule. The two variables named x in the two modules both point to the same value cell. Thus, if you set x to a new value in one module, its value is changed in both modules. Although the name is exported from one module and imported into the other, access to its value cell is completely symmetric-it can be set or get from either module.

Figure 9-2: Importing names into modules

When you specify that one module uses another module, you can control which names from that module you want to import. You can also rename imported names in your own module, to prevent naming clashes, by prefixing the imported names with a set of characters or by simply giving the variables entirely different names. A renamed variable maintains its original definition. In effect, a ScriptX variable can have a different name in each module. Finally, imported names can also be re-exported, effectively "passing them along" to any modules that use the module that re-exported them.

Figure 9-3: Importing names into a module, with a prefix

Name Bindings

A module is a collection of name bindings that the compiler uses at a particular point in the execution of a program. ScriptX name bindings allow for the separation of names and objects, so that objects can have different names in different modules. A different name can be "bound" to a given variable in each module in which it is visible.

A binding is an association between a name and a value cell in memory. A value cell stores a pointer to an object somewhere in memory. Bindings act as a level of indirection between names and value cells. This indirection is what allows a ScriptX program to run with different namespaces. When the ScriptX compiler switches from one module to another, it is actually switching from one set of bindings to another.

Think of a binding as a way to hang on to an object. Objects do not have to be associated with bindings. References to objects are embedded within other objects, often many levels deep. Expressions that assign a new global name, such as an assignment, function, or class expression, create a binding.

The following example demonstrates what bindings are created when a simple ScriptX program runs. Although it is not a complete technical description, it shows in a heuristic way how objects are associated with names, through bindings, and how names are used in a ScriptX program to get access to objects. ScriptX manages a global namespace (there is one global namespace for each module), by keeping a table of name bindings.

class KittyCat ()
	inst vars 
		favoriteFood
end
-- create an instances of Cat
object kiri (KittyCat)
	settings favoriteFood:"tuna"
end
KittyCat@0xe938c8

This program creates four name bindings. The first one is used to get access to the newly created KittyCat class, which is itself an object. The next two bindings are associated with instance variable access. Instance variable access is actually through generic getter and setter functions. These generic functions call the appropriate getter and setter methods. ScriptX automatically creates entries for generics in the module's name table. Finally, the script creates an object, an instance of KittyKat. This object is associated with a binding, bringing the total of new bindings in the system to four.

The following is a list of bindings that have been created so far in the execution of this program. Names in the first column are associated with objects, including classes and generic functions, in the second column. Lexical names are in lower case because they are interned in their downcase form. (In the scripter, you don't have to be concerned with case; you are free to use uppercase and lowercase to make your scripts more understandable.)

Lexical Name

Points To

kittycat

the KittyCat class

favoritefoodgetter

a generic function

favoritefoodsetter

a generic function

kiri

a KittyCat object at address 0xe938c8.

Note that on initialization, the favoriteFood instance variable slot for the kiri object is filled with a string constant (that is, with a pointer to a StringConstant object). This string constant has no bindings of its own. A script can only get and set the contents of instance variables through some binding or bindings. A simple instance variable access is actually a generic function call, which uses the bindings defined for the object and for the generic functions it provides a method for.

kiri.favoriteFood
"tuna"

The instance variable access expression kiri.favoriteFood is translated into a call to the getter generic function that is associated with this instance variable. Two bindings are required to get a value-one to the generic function and one to the object itself.

favoriteFoodGetter kiri

Each instance variable is really associated with two name bindings, one for its getter and one for its setter. If an instance variable of the same name exists elsewhere in the current module, it shares the same binding. A generic function handles method dispatch, routing calls to a getter or setter to the method defined by the appropriate object. This allows the same instance variable name to be used by many different objects within a module.

This chapter discusses syntax for importing and exporting variables in modules. What really happens in ScriptX is that a module imports and exports a set of name bindings. As a result, ScriptX modules do not just share values-they share value cells.

Defining Variables Within Modules

Each variable can have only one definition across modules, although that variable can be given a different name when it is imported into another module. The module that provides a definition for that module is considered to own that variable, and only the module that owns it can redefine it. When a module is stored into the ScriptX object store, all the variables that it owns and their most recent values are stored with it.

The only truly global names are module names. All variables are declared within some module. A variable can only be accessed (referenced) within some module. Since a given value cell can be bound to different names in different modules, and since the same name can be used to denote different variables in different modules, you do not know what variable something is until you know what module it was compiled in.

A variable is owned by the module that defines it. Only one module owns each variable. The ScriptX class definition, object definition, function definition, and global assignment expressions (a global assignment is a global declaration and assignment all in one expression) all declare and define a variable. Note that a variable defined when an object is assigned to a name. This name is usually declared as a name and assigned a value in the same expression, as in a class, object, or function declaration.

A module does not have to be the module that defines a variable to export a name for that variable. The ScriptX module system allows variables to be exported from one module, and then imported and defined in another module. That second module, the one that defines the variable, does not have to re-export the variable for that definition to be visible, nor does a module that wants to use that variable need to use the module that defined it. By using the module that exported the variable, its definition comes along automatically. This separation between an exported variable and its definition allows you to organize and build networks of modules which can include circular use relationships between modules, multiple interfaces to the same module, and more comprehensible relationships amongst complex networks of modules that use and export many different variables. "Organizing Modules" on page 214 describes how to use modules in this way.

A single variable can be exported, imported, and renamed in different modules. Any module with access to that variable can change its value, if that variable has not been declared constant. However, only one module is considered to own that variable. When a module is stored in the ScriptX object store, it stores all variables it owns with their current values. If a variable is not owned by any modules, it cannot be stored with any of those modules.

The module that owns a variable is the module that provides its definition. A variable that is not defined in any module is not owned by any module.

The class, object, and function declaration expressions declare and assign the variable in one expression, allowing them to be owned by the module that defined them. You can separate declaration and assignment for both objects and functions. For classes, the name must be specified when the class is declared, and that name is automatically declared constant. Thus, a class name is always owned by the module in which it is declared.

A global variable that you create and assign by hand (using an assignment expression) requires two conditions to be owned by a module: you must both declare it and assign it in the same expression:

global myVariable := "variable's value"

Only declaration and assignment in the same expression confer ownership of a variable; declaring and assigning the variable in different expressions is not the same:

global anotherVariable
anotherVariable := "another value" -- anotherVariable is not owned

A variable that is declared but not assigned any value does not get saved with a module-the module does not yet own that variable. Note that when you define a variable in a ScriptX program you do not necessarily have to give it the same value it will hold when the module is stored. When modules are stored in the ScriptX object store, the module stores the most recent value for each variable that it owns. To make sure a variable is owned by a module without having to assign it its proper value right away, give it a value of undefined at declaration time:

global VarToBeChanged := undefined

Later on in the program, you can assign the value you want to be stored.

A module maintains its own table of name bindings. To export a variable is to allow other modules to have a binding for a particular value cell. To say that a particular module defines a variable means that its value cell was created in that module. The value cell is created when the variable is assigned a value. Other modules that have access to the variable have access only through the value cell in the module where it is defined. To import a variable into a module is to have a binding for that variable in the current module. Other options for using modules, such as excludes, prefix, and renames, also operate on this table of bindings.

If a module attempts to define a variable that is already shared, an error message results and the variable is not redefined. If a variable is exported (shared), then it is still only one variable (not a copy) and can have only one definition. For example, if a shared variable is defined in one module, assigned to in another module, and read in a third module, the third module reads the value that was assigned by the second module.

A shared variable does not have to have the same name in each module that uses it. You can use features like prefix and renames to give the variable multiple names, but it is still only one variable (not a copy) and has only one definition. Different modules may use different names, but they are still accessing the same location in memory.

If a variable is not exported, then you can use the same identifier to define a separate variable in another module. These variables have the same name within each module, but they refer to different locations in memory.

In this respect, a variable is really just a collection of name bindings, one for each module in which the variable is visible. A variable continues to exist and to occupy memory when the module it is defined in is no longer the module that the compiler is currently using. A variable is visible to scripts in the current module if the current module has a binding for it.

Examples-Ownership of Variables

ScriptX distinguishes between declaring a name and defining a variable. In practice the declaration of a name and the definition of a variable usually occur in the same expression, but the distinction is important.

The global expression declares a ScriptX lexical name. A name that is declared global is visible only within the module in which it is declared, unless that name is exported. In this way, the same lexical name can be declared in different modules.

In the following example, the module Europe exports four names: finland, albania, portugal, and ireland.

module Europe
	uses ScriptX
	exports finland, albania, portugal, ireland
end
in module Europe
global finland, albania:19, portugal := 27

When the global expression is combined with an assignment expression, the resulting expression both declares a lexical name and defines a variable. Since albania and portugal are assigned values, albania and portugal are both declared as names and defined as a variables.

At this point, a program can access the values of albania and portugal, since both names have been defined as variables. Although finland is declared as a name, it is not assigned a value, so finland is not defined as a variable. Likewise, ireland has not been assigned a value, so it cannot be accessed as a variable. Since ireland has not been declared either, assigning a value to ireland will cause the compiler to issue a warning. By contrast, finland has been declared, so the compiler does not issue a warning when a value is assigned to finland.

If compilation and execution switch to another module that uses the Europe module, the variables that are defined in Europe are visible there.

module World
	uses ScriptX, Europe
end
in module World
albania
19
portugal
27

At this point in the execution of the program, finland and ireland have still not been defined as variables. When they are assigned values in the World module, they are defined in the World module. Even though finland was declared in the Europe module, and ireland was exported by Europe, but not declared, both variables are now defined in World.



in module World 
finland := 11
ireland := 94

If compilation reverts to the Europe module, finland and ireland retain their definitions as variables. They are visible in both Europe and World.

in module Europe
finland
11
ireland
94

Although all four of the variables in this example are exported by Europe, and three of them are declared there, it is where they are defined that determines how they are saved. Since albania and portugal are defined in the Europe module, they are saved to the object store with Europe. By contrast, finland and ireland are actually defined in the World module.

Only declaration and definition in the same module confers ownership. Since finland is not declared in the World module, it cannot be owned by any module, and is not saved. But ireland, even though it was exported by Europe, is declared and defined in the World module. Thus, ireland is saved with the World module.

The object, class, and function definition expressions implicitly declare a global name and define a variable as well, creating a binding for that variable in the current module.

module Universe
	uses ScriptX
end
in module Universe
class Star () end
object sun (Star) end
function orbit a b -> 
	format debug "1%* orbits 2%*\n" #(a, b) @normal

In this example, sun, star, and orbit are declared as lexical names and defined as variables (a class definition is a constant). Each of these variables has a binding in the Universe module, meaning that a lexical name is associated with the variable in that module. Since sun, star, and orbit are defined as variables in Universe, they can be saved to the object store with Universe, and they can be exported to other classes.


This document is part of the ScriptX Language Guide, one of the volumes of the ScriptX Technical Reference Series. ScriptX is developed by the ScriptX Engineering Team at Apple Computer, successor to the Kaleida Engineering Team at Kaleida Labs, Inc.

Copyright 1996 Apple Computer, Inc. All Rights Reserved.